﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.Windows.Controls;
using Plugins.Windows;
using System.Windows.Documents;
using System.Windows;
using System.Windows.Media;
using System.Xml.XPath;

namespace XPathPlugin
{
    class XPathGraphics
    {
        #region Variables

        private int _nodeNumber = 1;

        private XmlDocument _dom;
        private XmlNamespaceManager _xmlNM;
        private TreeListView _view;
        private RichTextBox _expressionBox;
        Dictionary<XmlNode, TreeListViewItem> _nodeMapping;

        public event EventHandler<EventArgs> Showed;

        private static readonly Brush _ElementBrush = new SolidColorBrush(Colors.DarkBlue);        

        #endregion

        public XPathGraphics(XmlDocument dom, XmlNamespaceManager xmlNM, TreeListView view, Dictionary<XmlNode, TreeListViewItem> nodeMapping)
        {
            _dom = dom;
            _xmlNM = xmlNM;
            _view = view;            

            _nodeMapping = nodeMapping;

            if (dom == null || view == null || nodeMapping == null)
                return;

            _nodeMapping.Clear();
        }

        /// <summary>
        /// Shows Xml file to TreeListView
        /// </summary>
        public void Show()
        {
            if (_view != null && _dom != null)
            {
                _view.BeginInit();
                
                _view.Items.Clear();

                _nodeNumber = 1;
                

                // Gets root node from XML
                XmlNode rootNode = _dom.DocumentElement;
                TreeListViewItem rootView = new TreeListViewItem();

                // If no root node
                if (rootNode == null)
                {
                    _view.EndInit();
                    return;
                }

                // Sets graphics of node
                rootView.Foreground = _ElementBrush;
                rootView.FontWeight = FontWeights.Bold;
                rootView.Content = "<" + XmlNode2String(rootNode) + ">";
                rootView.LineNumber = _nodeNumber++;

                // add new TreeListViewItem to TreeListView
                _view.Items.Add(rootView);

                // add XmlNode, TreeListViewItem to mapping dictionary
                _nodeMapping.Add(rootNode, rootView);

                // Loads rest of nodes of XML file
                AddNodes(rootNode, rootView);

                _view.ExpandAll();
                               
                _view.EndInit();
            }
            if (Showed != null)
                Showed(this, EventArgs.Empty);
        }       

        /// <summary>
        /// Show plugin menu
        /// </summary>
        /// <param name="pluginMenu">menu to show</param>
        /// <param name="pluginMenuName">menu name</param>
        /// <param name="showTreeClicked">Eventhandler for pluginMenu.Click event</param>
        public static void ShowPluginMenu(MenuItem pluginMenu, string pluginMenuName, RoutedEventHandler showTreeClicked) 
        {
            if (pluginMenu == null || pluginMenuName == null || showTreeClicked == null)
                return;

            pluginMenu.BeginInit();

            MenuItem showTreeItem = new MenuItem();
            showTreeItem.Click += showTreeClicked;
            showTreeItem.Header = "Show Tree";           
            showTreeItem.Background = Brushes.White;


            pluginMenu.Background = Brushes.White;
            pluginMenu.Visibility = Visibility.Visible;
            pluginMenu.Header = pluginMenuName;
            pluginMenu.Items.Add(showTreeItem);

            pluginMenu.EndInit();
            ((Menu)pluginMenu.Parent).UpdateLayout(); ;
        }

        /// <summary>
        /// Add rest of XmlNodes to TreeListView
        /// </summary>
        /// <param name="rootNode">XmlNode</param>
        /// <param name="rootView">TreeListViewItem item</param>
        private void AddNodes(XmlNode rootNode, TreeListViewItem rootView)
        {
            if (rootNode == null || rootView == null)
                return;

            // Register BrakePointChanged event
            rootView.BrakePointChanged += _view.BrakePointChangedHandler;

            if (rootNode.HasChildNodes)
            {
                XmlNodeList xNodeList = rootNode.ChildNodes;
                TreeListViewItem tNode;

                // Add each child of node to TreeListView
                foreach (XmlNode xNode in xNodeList)
                {
                    tNode = new TreeListViewItem();

                    switch (xNode.NodeType)
                    {
                        case XmlNodeType.Element:
                            // Set graphics
                            tNode.Content = "<" + XmlNode2String(xNode) + ">";
                            tNode.Foreground = _ElementBrush;
                            tNode.FontWeight = FontWeights.Bold;

                            // Set LineNumber of current tNode (TreeListViewItem)
                            tNode.LineNumber = _nodeNumber++;

                            // Add TreeListViewItem as child of rootView (TreeListViewItem)
                            rootView.Items.Add(tNode);

                            // Add XmlNode, TreeListViewItem to mapping dictionary
                            _nodeMapping.Add(xNode, tNode);

                            // Recursive call for next child of current xNode
                            AddNodes(xNode, tNode);
                            break;

                        default:
                            // Set graphics
                            tNode.Content = NormalizeElementText(rootNode.InnerText.Trim());
                            tNode.FontWeight = FontWeights.Normal;

                            // Set LineNumber of current tNode (TreeListViewItem)
                            tNode.LineNumber = _nodeNumber++;

                            // Add TreeListViewItem as child of rootView (TreeListViewItem)
                            rootView.Items.Add(tNode);
                            break;
                    }
                }
            }
        }

        /// <summary>
        /// Converts XmlNode to string.
        /// </summary>
        /// <param name="node">XmlNode to convert.</param>
        /// <returns>string</returns>
        private string XmlNode2String(XmlNode node)
        {
            if (node == null)
                return "";

            string str = ""; 
           
            // Add namespace to XmlNameSpaceManager
            if (node.Prefix.Length > 0)
                _xmlNM.AddNamespace(node.Prefix, node.NamespaceURI);

            // Add node name to string
            str += node.Name;                

            // if some attributes found, add to string attribute's name and value
            foreach (XmlAttribute nodeAttr in node.Attributes)
            {           
                str += " " + nodeAttr.Name + "=" + "'" + nodeAttr.Value + "',";
            }

            return node.Attributes.Count != 0 ? str.Substring(0, str.Length - 1) : str;
        }

        /// <summary>
        /// Normalize text. Removes all \n, \t, \v, \f, \r
        /// </summary>
        /// <param name="text">Text to normalize</param>
        /// <returns>Normalized string</returns>
        private static string NormalizeElementText(string text)
        {
            if (text == null)
                return "";

            return text.Replace("\n", "")
                       .Replace("\t", "")
                       .Replace("\v", "")
                       .Replace("\f", "")
                       .Replace("\r", "");
        }        
    }
}
